// Copyright (c) 2025, 大连理工大学 (Dalian University of Technology)
//
// Licensed under the Mulan PSL v2.
// You can use this file according to the terms and conditions of the Mulan PSL v2.
// You may obtain a copy of the License at
//
//     http://license.coscl.org.cn/MulanPSL2
//
// THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
// EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. SEE THE MULAN PSL v2
// FOR MORE DETAILS.

#include "DBManager/Repository/HDF5/Input/Property/PCOMPRepository.h"

#include "DBManager/IRepository/Input/Property/IPropertySummaryRepository.h"
#include "DBManager/Repository/HDF5/Base/HDF5RepositoryMacro.h"

HDF5_REG_REPOSITORY (PCOMPRepository)

using namespace SG::DBManager::HDF5;

PCOMPRepository::PCOMPRepository (HDF5Database& database) : MainlyRepositoryBase<SG::DataStructure::FEM::PCOMPData> (database)
{}

void PCOMPRepository::InitialDatasetInfo ()
{
    RegisterIndex ("/OWN/INDEX/INPUT/PROPERTY/PCOMP", IndexDataset, GetType ());
    RegisterTableInfo<PCOMP::IDENTITY> ("/SG//INPUT/PROPERTY/PCOMP/IDENTITY", MainDataset);
    RegisterTableInfo<SG::DataStructure::FEM::ShellSectionLayer> ("/SG//INPUT/PROPERTY/PCOMP/PLY", PLY);
    RegisterTableInfo<SG::DataStructure::Common::I32> ("/SG//INPUT/PROPERTY/PCOMP/LAM", LAM);

    // 连接信号槽 当调用汇总的时候，将已有的缓存信息全部都持久化到HDF5文件中。
    GetDatabase ().GetHDF5Hub()->m_propertySummarySignal.connect ([this] { Sort (); });
}

void PCOMPRepository::DumpWriteCahce ()
{
    auto& writeStorage = this->GetMainCache ().m_writeStorage;

    // 设置为无顺序
    GetMainDatasetInfo ().SetIsSequential (false);

    std::vector<PCOMP::IDENTITY>                           IDENTITYData;
    std::vector<SG::DataStructure::FEM::ShellSectionLayer> PLYData;
    std::vector<SG::DataStructure::Common::I32>            LAMData;  // SYM表单

    OwnToNastran (writeStorage, IDENTITYData, PLYData, LAMData);

    WriteData (MainDataset, IDENTITYData);
    WriteData (PLY, PLYData);
    WriteData (LAM, LAMData);

    // 更新Index表
    WriteIndexData (this->GetIndexDatasetInfo (), writeStorage);

    writeStorage.clear ();
}

std::vector<PCOMPRepository::DataType>*  PCOMPRepository::LoadReadCache (std::size_t index)
{
    std::vector<PCOMP::IDENTITY> IDENTITYData;
    LoadMainMetadata (index, IDENTITYData);

    if (IDENTITYData.empty ())
    {
        return nullptr;
    }

    // 读取范围内的附表信息
    std::vector<SG::DataStructure::FEM::ShellSectionLayer> PLYData;
    LoadSecondaryMetadata (IDENTITYData, PLY_POS, PLY_LEN, PLY, PLYData);

    // lam
    std::vector<SG::DataStructure::Common::I32> LAMData;
    GetDataset (LAM).select ({ this->GetMainCache ().dataMinIndex }, { IDENTITYData.size () }).read (LAMData);

    auto* cacheVector = m_cacheHelper.LookupOrCreateVector<DataType> (GetMainCache ().m_readCacheKey, IDENTITYData.size ());
    NastranToOwn (IDENTITYData, PLYData, LAMData, *cacheVector);
    return cacheVector;
}

void PCOMPRepository::SortData ()
{
    // 获取所有数据 👇
    std::vector<PCOMP::IDENTITY> IDENTITYData;
    GetDataset (MainDataset).read (IDENTITYData);

    if (IDENTITYData.empty ())
    {
        GetMainDatasetInfo ().SetIsSequential (true);
        return;
    }

    std::vector<SG::DataStructure::FEM::ShellSectionLayer> PLYData;
    GetDataset (PLY).read (PLYData);

    std::vector<SG::DataStructure::Common::I32> LAMData;
    GetDataset (LAM).read (LAMData);

    // 将所有数据转换
    std::vector<SG::DataStructure::FEM::PCOMPData> AllData;
    NastranToOwn (IDENTITYData, PLYData, LAMData, AllData);

    // 排序
    std::sort (AllData.begin (), AllData.end ());

    // 转换回 Nastran格式
    OwnToNastran (AllData, IDENTITYData, PLYData, LAMData, true);

    // 写出数据至文件
    GetDataset (MainDataset).write (IDENTITYData);
    GetDataset (PLY).write (PLYData);
    GetDataset (LAM).write (LAMData);

    // 重写索引
    RewriteIndexData (AllData);

    // 设置为已顺序
    GetMainDatasetInfo ().SetIsSequential (true);
}

std::vector<SG::DataStructure::FEM::PCOMPData> PCOMPRepository::GetAllData ()
{
    Sort ();

    std::vector<SG::DataStructure::FEM::PCOMPData> result;

    // 获取所有数据 👇
    std::vector<PCOMP::IDENTITY> IDENTITYData;
    GetDataset (MainDataset).read (IDENTITYData);

    if (IDENTITYData.empty ())
    {
        return result;
    }

    std::vector<SG::DataStructure::FEM::ShellSectionLayer> PLYData;
    GetDataset (PLY).read (PLYData);

    std::vector<SG::DataStructure::Common::I32> LAMData;
    GetDataset (LAM).read (LAMData);

    // 将所有数据转换
    NastranToOwn (IDENTITYData, PLYData, LAMData, result);

    return result;
}

void PCOMPRepository::OwnToNastran (std::vector<SG::DataStructure::FEM::PCOMPData>&              own,
                                    std::vector<PCOMP::IDENTITY>& _OUT                           IDENTITYData,
                                    std::vector<SG::DataStructure::FEM::ShellSectionLayer>& _OUT PLYData,
                                    std::vector<SG::DataStructure::Common::I32>& _OUT            LAMData,
                                    bool                                                         isSort)
{
    IDENTITYData.clear ();
    PLYData.clear ();
    LAMData.clear ();
    IDENTITYData.reserve (own.size ());
    LAMData.reserve (own.size ());

    // 获取已有数据条数，主要是放在主表的POS字段
    // 如果是重新排序的时候，Pos都是从0 开始的
    size_t PLYCount = isSort ? 0 : this->GetDatasetInfo (PLY).GetRowCount ();

    // OwnIdent 代表咱们自己的数据结构
    // NasIdent 代表nastran的数据结构
    for (auto& OwnIdent : own)
    {
        // 写入PLY
        PLYData.insert (PLYData.end (), OwnIdent.m_secData.begin (), OwnIdent.m_secData.end ());

        const size_t len = OwnIdent.m_secData.size ();

        // 写入IDENTITY
        PCOMP::IDENTITY identity{};
        identity.PID       = OwnIdent.m_id;
        identity.NPLIES    = OwnIdent.m_nplies;
        identity.Z0        = OwnIdent.m_offset;
        identity.NSM       = OwnIdent.m_nsm;
        identity.SB        = OwnIdent.m_sb;
        identity.FT        = OwnIdent.m_ft;
        identity.TREF      = OwnIdent.m_tref;
        identity.GE        = OwnIdent.m_ge;
        identity.PLY_POS   = static_cast<SG::DataStructure::Common::I64> (PLYCount);
        identity.PLY_LEN   = static_cast<SG::DataStructure::Common::I64> (len);
        identity.DOMAIN_ID = OwnIdent.m_domainId;
        IDENTITYData.emplace_back (identity);

        // 写入LAM
        LAMData.emplace_back (OwnIdent.m_lam);

        PLYCount += len;
    }
}

void PCOMPRepository::NastranToOwn (std::vector<PCOMP::IDENTITY>&                           IDENTITYData,
                                    std::vector<SG::DataStructure::FEM::ShellSectionLayer>& PLYData,
                                    std::vector<SG::DataStructure::Common::I32>&            LAMData,
                                    std::vector<SG::DataStructure::FEM::PCOMPData>& _OUT    own)
{
    own.clear ();
    own.reserve (IDENTITYData.size ());

    // 代表初始的Index 位置, 因为数据有可能是截取了附表的一部分，所有主表中根据POS找的时候需要对应一下，
    // 比如第一个主表数据中POS字段为10000， 附表的vector的数据 就是从附表中的第10000个开始对应的 也就是Index==0的
    const SG::DataStructure::Common::I64 plyIndex = IDENTITYData[0].PLY_POS;

    int index = 0;  // NOLINT(misc-const-correctness)

    for (auto&& NasIdent : IDENTITYData)
    {
        SG::DataStructure::FEM::PCOMPData OwnIdent;
        OwnIdent.m_id       = NasIdent.PID;
        OwnIdent.m_nplies   = NasIdent.NPLIES;
        OwnIdent.m_offset   = NasIdent.Z0;
        OwnIdent.m_nsm      = NasIdent.NSM;
        OwnIdent.m_sb       = NasIdent.SB;
        OwnIdent.m_ft       = NasIdent.FT;
        OwnIdent.m_tref     = NasIdent.TREF;
        OwnIdent.m_ge       = NasIdent.GE;
        OwnIdent.m_lam      = static_cast<SG::DataStructure::FEM::LaminateOptions> (LAMData[index]);
        OwnIdent.m_domainId = NasIdent.DOMAIN_ID;
        OwnIdent.m_secData.reserve (NasIdent.PLY_LEN);
        OwnIdent.m_secData.assign (PLYData.begin () + NasIdent.PLY_POS - plyIndex,
                                   PLYData.begin () + NasIdent.PLY_POS + NasIdent.PLY_LEN - plyIndex);

        own.emplace_back (OwnIdent);
        index++;
    }
}
